#include "MCLinearDimensionVisual.h"
#include <MCSettings.h>
#include <MCPoint2D.h>
#include <iostream>
#include <MCUtils.h>
#include <QTextItem>
#include <MCMathUtils.h>
#include <MCUtils.h>
#include <MCGripPoint.h>
#include <MCMathUtils.h>
#include <qnamespace.h>

MCLinearDimensionsVisual::MCLinearDimensionsVisual(ICanvas *canvas, MCLinearDimension2D dimension) :
    MCGraphicsItem(canvas),
    _dimensionModel(dimension)
{
    initGripPoints();
}

void MCLinearDimensionsVisual::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    setPainter(painter);

    drawDimension(painter);
}

QPainterPath MCLinearDimensionsVisual::shape() const
{
    QPainterPath boundings = lineBoundigs(transformed().dimensionLine());
    boundings.connectPath(lineBoundigs(MCLineSegment2D(transformed().dimensionLine().startPoint(), transformed().sourceLine().startPoint())));
    boundings.connectPath(lineBoundigs(MCLineSegment2D(transformed().dimensionLine().endPoint(), transformed().sourceLine().endPoint())));

    return boundings;
}

void MCLinearDimensionsVisual::setGripAt(IGripPoint *grip, const MCPoint2D &pos)
{
    if (grip->index() == 0)
    {
        _dimensionModel.update(pos, _dimensionModel.sourceLine().endPoint());
    }
    else if (grip->index() == 1)
    {
        _dimensionModel.update(_dimensionModel.sourceLine().startPoint(), pos);
    }
    else if (grip->index() == 2)
    {
        double height = pos.distanceTo(_dimensionModel.dimensionLineNoTransform().startPoint());
        _dimensionModel.setHeight(height);
    }
    else if (grip->index() == 3)
    {
        double height = pos.distanceTo(_dimensionModel.dimensionLineNoTransform().endPoint());
        _dimensionModel.setHeight(height);
    }

    resetGrips();
}

void MCLinearDimensionsVisual::resetGrips()
{
    gripPoints[0]->update(_dimensionModel.sourceLine().startPoint());
    gripPoints[1]->update(_dimensionModel.sourceLine().endPoint());
    gripPoints[2]->update(_dimensionModel.inversedDimensionLine().startPoint());
    gripPoints[3]->update(_dimensionModel.inversedDimensionLine().endPoint());
}

void MCLinearDimensionsVisual::update(const MCPoint2D &p1, const MCPoint2D &p2)
{
    _dimensionModel.update(p1, p2);
}

void MCLinearDimensionsVisual::update(const MCPoint2D &p1, const MCPoint2D &p2, double height)
{
    update(p1, p2);
    _dimensionModel.setHeight(height);
}

MCLinearDimension2D MCLinearDimensionsVisual::linearDimension() const
{
    return _dimensionModel;
}

void MCLinearDimensionsVisual::initGripPoints()
{
    gripPoints.push_back(new MCGripPoint(this, StartPoint, 0, _dimensionModel.sourceLine().startPoint()));
    gripPoints.push_back(new MCGripPoint(this, EndPoint, 1, _dimensionModel.sourceLine().endPoint()));
    gripPoints.push_back(new MCGripPoint(this, StartPoint, 2, _dimensionModel.inversedDimensionLine().startPoint()));
    gripPoints.push_back(new MCGripPoint(this, EndPoint, 3, _dimensionModel.inversedDimensionLine().endPoint()));
}

MCLinearDimension2D MCLinearDimensionsVisual::transformed() const
{
    MCPoint2D p1 = presenter->screenToWorld(_dimensionModel.sourceLine().startPoint());
    MCPoint2D p2 = presenter->screenToWorld(_dimensionModel.sourceLine().endPoint());
    double height = _dimensionModel.height() / presenter->scale();
    MCLinearDimension2D result(p1, p2);
    result.setHeight(height);
    result.setAlligment(_dimensionModel.alligment());

    return result;
}

void MCLinearDimensionsVisual::drawDimension(QPainter *painter)
{
    MCLineSegment2D sourceLine(transformed().sourceLine());
    MCLineSegment2D dimensionLine(transformed().dimensionLine());

    drawLeaderLines(sourceLine, dimensionLine);
    drawArrows(dimensionLine.startPoint(), dimensionLine.endPoint());
    drawLengthText(painter);
}

void MCLinearDimensionsVisual::drawLeaderLines(MCLineSegment2D sourceLine, MCLineSegment2D dimensionLine)
{
    drawLineSegment(sourceLine.startPoint(), dimensionLine.startPoint());
    drawLineSegment(sourceLine.endPoint(), dimensionLine.endPoint());
}

void MCLinearDimensionsVisual::drawArrows(MCPoint2D p1, MCPoint2D p2)
{
    if (_dimensionModel.length() > MCSettings::AxisArrowSize)
    {
        drawInArrows(p1, p2);
    }
    else
    {
        drawOutArrows(p1, p2);
    }
}

void MCLinearDimensionsVisual::drawLengthText(QPainter *painter)
{
    QString length = QString::number((int)_dimensionModel.length());
    QPoint center = QPoint(transformed().dimensionLine().midPoint().x, transformed().dimensionLine().midPoint().y);

    MCPoint2D topLeft = presenter->screenToWorld(MCPoint2D(250, 250));
    MCPoint2D rightBot = presenter->screenToWorld(MCPoint2D(300, 300));

    QRectF rotatedRect(QPointF(topLeft.x, topLeft.y), QPoint(rightBot.x, rightBot.y));

    painter->drawText(center, length); 
}

void MCLinearDimensionsVisual::drawInArrows(MCPoint2D p1, MCPoint2D p2)
{
    double size = MCSettings::DimensionsArrowSize;

    drawArrow2(p1, p2, size, 30);
    drawArrow2(p2, p1, size, 30);
}

void MCLinearDimensionsVisual::drawOutArrows(MCPoint2D p1, MCPoint2D p2)
{
    double arrowSize = MCSettings::DimensionsArrowSize;
    double arrowLineSize = MCSettings::DimensionOutArrowSize / presenter->scale();
    MCLineSegment2D dimensionLine = transformed().dimensionLine();

    MCVector2 direction = dimensionLine.direction().normalize() * arrowLineSize;
    MCVector2 direction2 = dimensionLine.direction().normalize() * arrowLineSize;
    MCPoint2D tempStartPoint = p1 - direction;
    MCPoint2D tempEndPoint = p2 + direction2;

    drawArrow2(tempEndPoint, p2, arrowSize, 30);
    drawArrow2(tempStartPoint, p1, arrowSize, 30);
}

QPainterPath MCLinearDimensionsVisual::lineBoundigs(const MCLineSegment2D &line) const
{
    static const qreal kClickTolerance = MCSettings::LineClickableZone;
    MCLineSegment2D drawing = line;
    auto ep = drawing.endPoint();
    auto sp = drawing.startPoint();

    QPointF end_p = QPointF(ep.x, ep.y);
    QPointF start_p = QPointF(sp.x, sp.y);

    QPointF vec = end_p - start_p;

    if (qSqrt(QPointF::dotProduct(vec, vec) != 0))
    {
        vec = vec * (kClickTolerance / qSqrt(QPointF::dotProduct(vec, vec)));
    }

    QPointF orthogonal(vec.y(), -vec.x());

    QPainterPath result(start_p - vec + orthogonal);
    result.lineTo(start_p - vec - orthogonal);
    result.lineTo(end_p + vec - orthogonal);
    result.lineTo(end_p + vec + orthogonal);
    result.closeSubpath();

    return result;
}